package spe.xml;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.wst.sse.core.internal.provisional.AbstractAdapterFactory;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import spe.model.registry.SPERegistryRegistry;
import spe.model.registry.SPEXmlAdapterRegistry;
import spe.Logger;
import spe.model.spem.SPEMElement;
import spe.model.spem.SPEMFactory;
import spe.model.spem.SPERootElement;

@SuppressWarnings("restriction")
public class SPEXmlAdapterFactory extends AbstractAdapterFactory
{
	private SPEMFactory semanticElementFactory;
	private SPEXmlAdapterRegistry xmlAdapterRegistry;
	
	//this hashmap holds adapters that has been registered
	private Map<SPEMElement, SPEXmlAdapter> adapterRegistry = new HashMap<SPEMElement, SPEXmlAdapter>();	
	private Document modelDocument;
	
	//dao anh vu
	private SPERootElement rootElement = null;
	
	public SPEXmlAdapterFactory(Document modelDoc, SPEMFactory elementFactory, SPERootElement rootE) 
	{
		super();
		this.modelDocument = modelDoc;
		this.semanticElementFactory = elementFactory;
		this.rootElement = rootE;
		xmlAdapterRegistry = SPERegistryRegistry.getXmlAdapterRegistry(elementFactory.getEditorId());
		setAdapterKey(this);
		setShouldRegisterAdapter(true);
	}
	
	public SPERootElement getRootSPEElement()
	{
		return rootElement;
	}
	
	public SPEXmlAdapter adapt(Node node) 
	{
		SPEXmlAdapter adapter = (SPEXmlAdapter)super.adapt((INodeNotifier)node);
		//System.out.println(String.format("[SPEXmlAdapterFactory] %s; %s ", adapter, node));
		return adapter;
	}
	
	//This method is called by method adapt to adapt
	protected INodeAdapter createAdapter(INodeNotifier target) 
	{
		
		if (modelDocument == null) throw new RuntimeException("The document property of the JpdlElementDomAdapterFactory is not initialized");
		SPEXmlAdapter result = null;
		try {
			IConfigurationElement element = xmlAdapterRegistry.getConfigurationElementByXmlNode((Node)target);
			//System.out.println(String.format("[SPEXmlAdapterFactory] Node %s; Element %s", target, element));
			if (element != null) 
			{
				result = (SPEXmlAdapter)element.createExecutableExtension("adapterClass");
			} 
			/*
			 * else if (((Node)target).getNodeType() != Node.TEXT_NODE){
				result = new GenericElementXmlAdapter();
			}*/
			
			if (result != null) 
			{
				result.setNode((Node)target);
				result.setFactory(this);
			}
			
		} catch (CoreException e) {
			throw new RuntimeException("Creation of executable extension failed", e);
		}
		return result;
	}
	
	private String calculateElementName(IConfigurationElement configurationElement, SPEMElement semanticElement) 
	{
		String elementName = configurationElement.getAttribute("xmlElement");
		if (elementName == null) {
			String nameProviderClass = configurationElement.getAttribute("nameProvider");
			if (nameProviderClass != null) {
				try {
					SPEXmlAdapterNameProvider nameProvider = (SPEXmlAdapterNameProvider)configurationElement.createExecutableExtension("nameProvider");
					if (nameProvider != null) {
						elementName = nameProvider.getName(semanticElement);
					}
				}
				catch (CoreException e) {
					Logger.logError("Problem creating nameProvider for " + semanticElement.getId(), e);
				}
			}
		}
		return elementName;
	}
	
	private SPEXmlAdapter createAdapter(IConfigurationElement configurationElement, String elementName) 
	{
		SPEXmlAdapter result = null;
		try {
			result = (SPEXmlAdapter) configurationElement.createExecutableExtension("adapterClass");
			INodeNotifier element = (INodeNotifier)modelDocument.createElement(elementName);
			element.addAdapter(result);
			result.setFactory(this);
			result = (SPEXmlAdapter)adapt(element);
			result.setNode((Node)element);
		} catch (CoreException e) {
		    Logger.logError("Unable to create XML Adapter for " + elementName, e);
		}
		return result;
	}
	
	public SPEXmlAdapter createAdapterFromModel(SPEMElement semanticElement) 
	{
		IConfigurationElement configurationElement = 
			xmlAdapterRegistry.getConfigurationElementBySemanticElementId(semanticElement.getElementId());
		//System.out.println(String.format("[SPEXmlAdapterFactory] Element Id: %s", semanticElement.getElementId()));
		String elementName = calculateElementName(configurationElement, semanticElement);
		if (elementName != null) {
			return createAdapter(configurationElement, elementName);
		} else {
			return null;
		}
	}
	
	void register(SPEXmlAdapter jpdlElementDomAdapter)
	{
		adapterRegistry.put(jpdlElementDomAdapter.getSemanticElement(), jpdlElementDomAdapter);
		
		/*
		System.out.println(String.format("[SPEXmlAdapterFactory]-----"));
		for(SPEXmlAdapter e: adapterRegistry.values())
			System.out.println(String.format("      %s", e));
		System.out.println(String.format("[SPEXmlAdapterFactory]-----"));
		*/
	}
	
	void unregister(SPEXmlAdapter jpdlElementDomAdapter) {
		adapterRegistry.remove(jpdlElementDomAdapter.getSemanticElement());
	}
	
	SPEXmlAdapter getRegisteredAdapterFor(SPEMElement speElement)
	{
		return (SPEXmlAdapter)adapterRegistry.get(speElement);
	}
	
	//Dao Anh Vu
	Map<SPEMElement, SPEXmlAdapter> getRegisteredAdapters(Class cls)
	{
		Map<SPEMElement, SPEXmlAdapter> subMap = new HashMap<SPEMElement, SPEXmlAdapter>(); 
		Iterator<SPEMElement> iter = adapterRegistry.keySet().iterator();
		while(iter.hasNext())
		{
			SPEMElement e = iter.next();
			if(cls.isInstance(e))
				subMap.put(e, adapterRegistry.get(e));
		}
		
		return subMap;
	}
	
	//Dao Anh Vu
	SPEMElement getRegisteredElementById(String speElementId)
	{
		Set<SPEMElement> set = adapterRegistry.keySet();
		for(SPEMElement e: set)
		{
			//System.out.println(String.format("[SPEXmlAdapterFactory] Element Id: %s; Looking for Id: %s", e, speElementId));
			if(e.getId().equals(speElementId))
				return e;
		}
		return null;
	}
	
	//Dao Anh Vu: create org.w3c.dom.Element
	Element createDOMElementFor(SPEMElement speElement)
	{
		IConfigurationElement configurationElement = 
				xmlAdapterRegistry.getConfigurationElementBySemanticElementId(speElement.getElementId());
		String elementName = configurationElement.getAttribute("xmlElement");
		return modelDocument.createElement(elementName);
	}
	
	String getDOMElementNameFor(SPEMElement speElement)
	{
		IConfigurationElement configurationElement = 
				xmlAdapterRegistry.getConfigurationElementBySemanticElementId(speElement.getElementId());
		String elementName = configurationElement.getAttribute("xmlElement");
		return elementName;
	}

	
	SPEMFactory getSemanticElementFactory() {
		return semanticElementFactory;
	}
	
	SPEXmlAdapterRegistry getXmlAdapterRegistry() {
		return xmlAdapterRegistry;
	}
}
